using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
using TangLiveRecorder.Models;

namespace TangLiveRecorder.Services
{
    /// <summary>
    /// 数据持久化服务类
    /// 负责应用程序数据的加载、保存和管理，包括设置、文件和直播信息
    /// 提供数据变化检测和文件冲突重试机制
    /// </summary>
    public class DataPersistenceService
    {
        /// <summary>
        /// 应用程序数据存储目录路径
        /// </summary>
        private readonly string _dataDirectory;
        
        /// <summary>
        /// 文件数据JSON文件路径
        /// </summary>
        private readonly string _filesDataPath;
        
        /// <summary>
        /// 直播数据JSON文件路径
        /// </summary>
        private readonly string _livesDataPath;
        
        /// <summary>
        /// 设置数据JSON文件路径
        /// </summary>
        private readonly string _settingDataPath;
        
        /// <summary>
        /// JSON序列化配置选项
        /// </summary>
        private readonly JsonSerializerOptions _jsonOptions;

        /// <summary>
        /// 缓存设置数据的哈希值，用于检测数据变化
        /// </summary>
        private string _lastSettingHash = string.Empty;

        /// <summary>
        /// 缓存文件数据的哈希值，用于检测数据变化
        /// </summary>
        private string _lastFilesHash = string.Empty;
        
        /// <summary>
        /// 缓存直播数据的哈希值，用于检测数据变化
        /// </summary>
        private string _lastLivesHash = string.Empty;

        /// <summary>
        /// 初始化数据持久化服务
        /// 设置数据文件路径、JSON序列化选项，并确保数据目录存在
        /// </summary>
        public DataPersistenceService()
        {
            // 设置应用程序数据目录为用户AppData下的TangLiveRecorder文件夹
            _dataDirectory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "TangLiveRecorder");
            _filesDataPath = Path.Combine(_dataDirectory, "files.json");
            _livesDataPath = Path.Combine(_dataDirectory, "lives.json");
            _settingDataPath = Path.Combine(_dataDirectory, "setting.json");

            // 配置JSON序列化选项：格式化输出、驼峰命名、宽松编码
            _jsonOptions = new JsonSerializerOptions
            {
                WriteIndented = true,
                PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
                Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping
            };

            // 确保数据目录存在，不存在则创建
            if (!Directory.Exists(_dataDirectory))
            {
                Directory.CreateDirectory(_dataDirectory);
            }
        }

        /// <summary>
        /// 加载设置数据
        /// </summary>
        /// <returns>设置信息对象，如果文件不存在或加载失败则返回默认设置</returns>
        public SettingInfo LoadSetting()
        {
            try
            {
                if (File.Exists(_settingDataPath))
                {
                    var json = File.ReadAllText(_settingDataPath);
                    var setting = JsonSerializer.Deserialize<SettingInfo>(json, _jsonOptions);
                    // 初始化哈希值
                    _lastSettingHash = ComputeHash(JsonSerializer.Serialize(setting, _jsonOptions));
                    Data.AddLog($"成功加载设置数据");
                    return setting ?? new SettingInfo();
                }
            }
            catch (Exception ex)
            {
                Data.AddLog($"加载设置数据失败: {ex.Message}");
            }

            var defaultSetting = new SettingInfo();
            _lastSettingHash = ComputeHash(JsonSerializer.Serialize(defaultSetting, _jsonOptions));
            return defaultSetting;
        }

        /// <summary>
        /// 加载文件数据
        /// </summary>
        /// <returns>文件列表</returns>
        public List<FileInfos> LoadFiles()
        {
            try
            {
                if (File.Exists(_filesDataPath))
                {
                    var json = File.ReadAllText(_filesDataPath);
                    var files = JsonSerializer.Deserialize<List<FileInfos>>(json, _jsonOptions) ?? new List<FileInfos>();
                    // 初始化哈希值
                    _lastFilesHash = ComputeHash(JsonSerializer.Serialize(files, _jsonOptions));
                    Data.AddLog($"成功加载 {files.Count} 个文件记录");
                    return files;
                }
            }
            catch (Exception ex)
            {
                Data.AddLog($"加载文件数据失败: {ex.Message}");
            }

            var defaultFiles = new List<FileInfos>();
            _lastFilesHash = ComputeHash(JsonSerializer.Serialize(defaultFiles, _jsonOptions));
            return defaultFiles;
        }

        /// <summary>
        /// 保存设置数据
        /// 如果数据未发生变化则跳过保存操作
        /// </summary>
        /// <param name="setting">要保存的设置数据</param>
        public void SaveSetting(SettingInfo setting)
        {
            // 检查数据是否发生变化，未变化则跳过保存
            if (!HasDataChanged(setting, ref _lastSettingHash))
            {
                return;
            }
            SaveWithRetry(_settingDataPath, setting, "设置");
        }

        /// <summary>
        /// 保存文件数据
        /// 如果数据未发生变化则跳过保存操作
        /// </summary>
        /// <param name="files">要保存的文件信息列表</param>
        public void SaveFiles(List<FileInfos> files)
        {
            // 检查数据是否发生变化，未变化则跳过保存
            if (!HasDataChanged(files, ref _lastFilesHash))
            {
                return;
            }
            SaveWithRetry(_filesDataPath, files, "文件");
        }

        /// <summary>
        /// 加载直播数据
        /// </summary>
        /// <returns>直播列表</returns>
        public List<LiveInfo> LoadLives()
        {
            try
            {
                if (File.Exists(_livesDataPath))
                {
                    var json = File.ReadAllText(_livesDataPath);
                    var lives = JsonSerializer.Deserialize<List<LiveInfo>>(json, _jsonOptions) ?? new List<LiveInfo>();
                    // 初始化哈希值
                    _lastLivesHash = ComputeHash(JsonSerializer.Serialize(lives, _jsonOptions));
                    Data.AddLog($"成功加载 {lives.Count} 个直播记录");
                    return lives;
                }
            }
            catch (Exception ex)
            {
                Data.AddLog($"加载直播数据失败: {ex.Message}");
            }

            var defaultLives = new List<LiveInfo>();
            _lastLivesHash = ComputeHash(JsonSerializer.Serialize(defaultLives, _jsonOptions));
            return defaultLives;
        }

        /// <summary>
        /// 保存直播数据
        /// 如果数据未发生变化则跳过保存操作
        /// </summary>
        /// <param name="lives">要保存的直播信息列表</param>
        public void SaveLives(List<LiveInfo> lives)
        {
            // 检查数据是否发生变化，未变化则跳过保存
            if (!HasDataChanged(lives, ref _lastLivesHash))
            {
                return;
            }
            SaveWithRetry(_livesDataPath, lives, "直播");
        }

        /// <summary>
        /// 异步保存文件数据
        /// 如果数据未发生变化则跳过保存操作
        /// </summary>
        /// <param name="files">要保存的文件信息列表</param>
        /// <returns>异步任务</returns>
        public async Task SaveFilesAsync(List<FileInfos> files)
        {
            // 检查数据是否发生变化，未变化则跳过保存
            if (!HasDataChanged(files, ref _lastFilesHash))
            {
                return;
            }
            await SaveWithRetryAsync(_filesDataPath, files, "文件");
        }

        /// <summary>
        /// 异步保存设置数据
        /// 如果数据未发生变化则跳过保存操作
        /// </summary>
        /// <param name="setting">要保存的设置数据</param>
        /// <returns>异步任务</returns>
        public async Task SaveSettingAsync(SettingInfo setting)
        {
            // 检查数据是否发生变化，未变化则跳过保存
            if (!HasDataChanged(setting, ref _lastSettingHash))
            {
                return;
            }
            await SaveWithRetryAsync(_settingDataPath, setting, "设置");
        }

        /// <summary>
        /// 异步保存直播数据
        /// 如果数据未发生变化则跳过保存操作
        /// </summary>
        /// <param name="lives">要保存的直播信息列表</param>
        /// <returns>异步任务</returns>
        public async Task SaveLivesAsync(List<LiveInfo> lives)
        {
            // 检查数据是否发生变化，未变化则跳过保存
            if (!HasDataChanged(lives, ref _lastLivesHash))
            {
                return;
            }
            await SaveWithRetryAsync(_livesDataPath, lives, "直播");
        }

        /// <summary>
        /// 异步带重试机制的文件保存方法
        /// 使用文件锁避免并发访问冲突，支持重试机制处理文件被占用的情况
        /// </summary>
        /// <typeparam name="T">要保存的数据类型</typeparam>
        /// <param name="filePath">目标文件路径</param>
        /// <param name="data">要保存的数据对象</param>
        /// <param name="dataType">数据类型名称，用于日志记录</param>
        /// <param name="maxRetries">最大重试次数，默认3次</param>
        /// <param name="retryDelayMs">重试间隔毫秒数，默认100毫秒</param>
        /// <returns>异步任务</returns>
        private async Task SaveWithRetryAsync<T>(string filePath, T data, string dataType, int maxRetries = 3, int retryDelayMs = 100)
        {
            for (int attempt = 1; attempt <= maxRetries; attempt++)
            {
                try
                {
                    var json = JsonSerializer.Serialize(data, _jsonOptions);

                    // 使用FileStream和文件锁来避免冲突
                    using (var fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None))
                    using (var writer = new StreamWriter(fileStream))
                    {
                        await writer.WriteAsync(json);
                    }

                    var count = data is ICollection collection ? collection.Count : 1;
                    Data.AddLog($"成功保存 {count} 个{dataType}记录");
                    return;
                }
                catch (IOException ex) when (ex.Message.Contains("being used by another process"))
                {
                    if (attempt == maxRetries)
                    {
                        Data.AddLog($"保存{dataType}数据失败: 文件被其他进程占用，已重试 {maxRetries} 次");
                        return;
                    }

                    Data.AddLog($"文件被占用，正在重试 ({attempt}/{maxRetries})...");
                    await Task.Delay(retryDelayMs);
                }
                catch (Exception ex)
                {
                    Data.AddLog($"保存{dataType}数据失败: {ex.Message}");
                    return;
                }
            }
        }

        /// <summary>
        /// 检测数据是否发生变化
        /// </summary>
        /// <typeparam name="T">数据类型</typeparam>
        /// <param name="data">要检查的数据</param>
        /// <param name="lastHash">上次的哈希值引用</param>
        /// <returns>如果数据发生变化返回true，否则返回false</returns>
        private bool HasDataChanged<T>(T data, ref string lastHash)
        {
            try
            {
                var json = JsonSerializer.Serialize(data, _jsonOptions);
                var currentHash = ComputeHash(json);

                if (currentHash == lastHash)
                {
                    return false;
                }

                lastHash = currentHash;
                return true;
            }
            catch (Exception ex)
            {
                Data.AddLog($"检测数据变化时出错: {ex.Message}");
                return true; // 出错时默认认为数据已变化
            }
        }

        /// <summary>
        /// 计算字符串的哈希值
        /// </summary>
        /// <param name="input">输入字符串</param>
        /// <returns>哈希值</returns>
        private string ComputeHash(string input)
        {
            using (var sha256 = System.Security.Cryptography.SHA256.Create())
            {
                var bytes = System.Text.Encoding.UTF8.GetBytes(input);
                var hashBytes = sha256.ComputeHash(bytes);
                return Convert.ToBase64String(hashBytes);
            }
        }

        /// <summary>
        /// 同步带重试机制的文件保存方法
        /// 使用文件锁避免并发访问冲突，支持重试机制处理文件被占用的情况
        /// </summary>
        /// <typeparam name="T">要保存的数据类型</typeparam>
        /// <param name="filePath">目标文件路径</param>
        /// <param name="data">要保存的数据对象</param>
        /// <param name="dataType">数据类型名称，用于日志记录</param>
        /// <param name="maxRetries">最大重试次数，默认3次</param>
        /// <param name="retryDelayMs">重试间隔毫秒数，默认100毫秒</param>
        private void SaveWithRetry<T>(string filePath, T data, string dataType, int maxRetries = 3, int retryDelayMs = 100)
        {
            for (int attempt = 1; attempt <= maxRetries; attempt++)
            {
                try
                {
                    var json = JsonSerializer.Serialize(data, _jsonOptions);

                    // 使用FileStream和文件锁来避免冲突
                    using (var fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None))
                    using (var writer = new StreamWriter(fileStream))
                    {
                        writer.Write(json);
                    }

                    var count = data is ICollection collection ? collection.Count : 1;
                    Data.AddLog($"成功保存 {count} 个{dataType}记录");
                    return;
                }
                catch (IOException ex) when (ex.Message.Contains("being used by another process"))
                {
                    if (attempt == maxRetries)
                    {
                        Data.AddLog($"保存{dataType}数据失败: 文件被其他进程占用，已重试 {maxRetries} 次");
                        return;
                    }

                    Data.AddLog($"文件被占用，正在重试 ({attempt}/{maxRetries})...");
                    Thread.Sleep(retryDelayMs);
                }
                catch (Exception ex)
                {
                    Data.AddLog($"保存{dataType}数据失败: {ex.Message}");
                    return;
                }
            }
        }

        /// <summary>
        /// 获取数据文件路径信息
        /// 用于外部组件获取数据存储位置
        /// </summary>
        /// <returns>包含数据目录、文件数据路径和直播数据路径的元组</returns>
        public (string DataDirectory, string FilesPath, string LivesPath) GetDataPaths()
        {
            return (_dataDirectory, _filesDataPath, _livesDataPath);
        }
    }
}